use crate::cache::IndexChange;
use crate::keys::VddKey;
use crate::{cache, field_db, model_db, user_db, CF_LOG, ROCKSDB};
use rust_rocksdb::WriteBatchWithTransaction;
use serde_json::json;
use std::sync::Arc;
use visit_dd_core::proj::{VddLog, VddLogDisplay, VddLogPart};
use visit_dd_core::query::{QueryDataPage, WebQueryParams};
use visit_dd_core::{Astr, UserId, AC_ADD};

pub async fn select_some_by_user_id(
    uid: &UserId,
    p: &WebQueryParams,
) -> QueryDataPage<VddLogDisplay> {
    let key = VddKey::IndexUser2ActionLog(uid.clone()).to_string();
    let ids = cache::get_index(&key);
    page_in_index(&ids, p.last_id, p.page_size as usize).await
}

async fn page_in_index(idx: &[u64], offset: u64, size: usize) -> QueryDataPage<VddLogDisplay> {
    let mut last_index = offset;
    let mut keys = Vec::<String>::new();
    for id in idx.iter().skip(last_index as usize) {
        last_index += 1;
        keys.push(VddKey::EntityActionLog(*id).to_string());
        if keys.len() >= size {
            break;
        }
    }
    let logs = select_some(&keys).await;
    QueryDataPage {
        data: logs,
        all_record_count: idx.len(),
        last_index,
    }
}

async fn select_some(keys: &[String]) -> Vec<Arc<VddLogDisplay>> {
    let mut logs = Vec::<Arc<VddLog>>::new();
    let cf_log = ROCKSDB.cf_handle(CF_LOG).unwrap();
    ROCKSDB
        .multi_get_cf(keys.iter().map(|k| (cf_log, k)))
        .iter()
        .for_each(|item| {
            if let Ok(Some(vu8)) = item {
                let log = serde_json::from_slice::<VddLog>(vu8).unwrap();
                logs.push(log.into());
            }
        });
    logs.sort_by(|a, b| b.log_ts.cmp(&a.log_ts)); //时间戳倒序
    let mut r = Vec::<Arc<VddLogDisplay>>::new();
    for x in logs.iter() {
        r.push(Arc::new(fill_text(x).await));
    }
    r
}

/// 分页获取日志
pub async fn select_some_by_model_id(p: &WebQueryParams) -> QueryDataPage<VddLogDisplay> {
    let key = VddKey::IndexModel2ActionLog(p.model_id.unwrap()).to_string();
    let ids = cache::get_index(&key);
    page_in_index(&ids, p.last_id, p.page_size as usize).await
}

async fn generate_text(al: &VddLog, show_id: bool) -> Astr {
    let mut log = String::new();
    let label_cache = cache::get_label_cache();
    for part in al.parts.iter() {
        match part {
            VddLogPart::LpAction(operator_id, action) => {
                let mut l = label_cache.get(&operator_id.0).map(|x| x.to_owned());
                if l.is_none() {
                    let u = user_db::u_load(operator_id, false, false).await;
                    l = Some(u.name);
                }
                if show_id {
                    log.push_str(&format!(
                        "用户 '{}'({}) {} ",
                        l.unwrap(),
                        operator_id.0,
                        action
                    ));
                } else {
                    log.push_str(&format!("用户 '{}' {} ", l.unwrap(), action));
                }
            }
            VddLogPart::LpModel(model_id) => {
                let mut l = label_cache.get(model_id).map(|x| x.to_owned());
                if l.is_none() {
                    let m = model_db::m_load(*model_id).await;
                    l = Some(m.name.to_owned());
                }
                if show_id {
                    log.push_str(&format!("模型 '{}'({}) ", l.unwrap(), *model_id));
                } else {
                    log.push_str(&format!("模型 '{}' ", l.unwrap()));
                }
            }
            VddLogPart::LpField(field_id) => {
                let mut l = label_cache.get(&field_id.0).map(|x| x.to_owned());
                if l.is_none() {
                    let f = field_db::f_load(field_id).await;
                    l = Some(f.label.clone().into());
                }
                if show_id {
                    log.push_str(&format!("的字段 '{}'({}) ", l.unwrap(), field_id.0));
                } else {
                    log.push_str(&format!("的字段 '{}' ", l.unwrap()));
                }
            }
            VddLogPart::LpRecord(record_id) => {
                if show_id {
                    log.push_str(&format!("的记录 ({}) ", record_id.0));
                } else {
                    log.push_str("的记录 ");
                }
            }
            VddLogPart::LpFile(file_key, file_name) => {
                if show_id {
                    log.push_str(&format!("文件 '{}' ({}) ", file_name, *file_key));
                } else {
                    log.push_str(&format!("文件 '{}' ", file_name));
                }
            }
            VddLogPart::LpVisitGroup(_, vg_id) => {
                log.push_str(&format!("的访问组 ({}) ", *vg_id));
            }
            VddLogPart::LpUser(u_id) => {
                let mut l = label_cache.get(&u_id.0).map(|x| x.to_owned());
                if l.is_none() {
                    let user = user_db::u_load(u_id, false, false).await;
                    l = Some(user.name.clone());
                }
                if show_id {
                    log.push_str(&format!("用户 '{}'({}) ", l.unwrap(), u_id.0));
                } else {
                    log.push_str(&format!("用户 '{}' ", l.unwrap()));
                }
            }
            VddLogPart::LpFvChange(old_value, new_value) => {
                log.push_str(&format!("旧值 '{}' 新值 '{}'", old_value, *new_value));
            }
            VddLogPart::LpRr(rr_id) => {
                log.push_str(&format!("'{}'", rr_id));
            }
        }
    }
    log.as_str().into()
}

pub(crate) fn save(
    batch: &mut WriteBatchWithTransaction<true>,
    index_changes: &mut Vec<IndexChange>,
    log: &VddLog,
) {
    let cf = ROCKSDB.cf_handle(CF_LOG).unwrap();
    batch.put_cf(
        &cf,
        VddKey::EntityActionLog(log.id).to_string(),
        json!(&log).to_string(),
    );
    log.parts.iter().for_each(|part| match part {
        VddLogPart::LpAction(uid, _) => {
            index_changes.push(IndexChange::new(
                VddKey::IndexUser2ActionLog(uid.clone()),
                AC_ADD,
                log.id,
            ));
        }
        VddLogPart::LpModel(model_id) => {
            index_changes.push(IndexChange::new(
                VddKey::IndexModel2ActionLog(model_id.clone()),
                AC_ADD,
                log.id,
            ));
        }
        _ => {}
    });
}

pub(crate) async fn fill_text(al: &VddLog) -> VddLogDisplay {
    VddLogDisplay {
        log_ts: al.log_ts.clone(),
        log: generate_text(al, false).await.clone(),
    }
}
